package app.neuroview;

import component.filedialog.AskFileDialog;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.ParseException;

import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JToggleButton;

import visualization.JNeuroCoordScatter;
import visualization.JNeuroView;
import visualization.NeuronJ3D;

import component.jfileexplorer.FileNode;
import component.jfileexplorer.JFileExplorer;
import export.CoordinateToMatlabExport;
import export.ImageToEPSExport;
import export.FileFilters;
import export.NeuronToHOCExport;
import export.NeuronToMatlabExport;
import export.StatisticsToMatlabExport;
import export.StatisticsToExcelExport;
import javax.swing.Box;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import meta.MetaInfo;

import neuron.Network;
import neuron.Neuron;
import neuron.NeuronFilter;
import neuron.NeuronStatistics;
import neuron.file.AuxData;
import neuron.file.NeuroLucidaLoader;

public class NeuroviewMain implements ActionListener {

	JFileExplorer jfe;
	JNeuroView view;
	
	
	@Override
	public void actionPerformed(ActionEvent e) {

		if (e.getActionCommand().equals("EXPORT.STATS")) {
			
			FileNode fn = jfe.getSelectedFile();
			if (fn == null || fn.getFile() == null || !fn.getFile().isDirectory()) {
				JOptionPane.showMessageDialog(view, "Please select a directory first!");
				return;
			}
			
			StatisticsToExcelExport se = new StatisticsToExcelExport();
			File dest = AskFileDialog.askExcelFile(fn.getFile(), view);
			if (dest == null) return;
			try {
				se.exportFilesInDir(fn.getFile(), dest);
				JOptionPane.showMessageDialog(view, "Export OK!");
			} catch (IOException ioe) {
				JOptionPane.showMessageDialog(view, ioe.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
				ioe.printStackTrace();
			}
		}

		else if (e.getActionCommand().equals("EXPORT.COORDS")) {
			
			FileNode fn = jfe.getSelectedFile();
			if (fn == null || fn.getFile() == null || !fn.getFile().isDirectory()) {
				JOptionPane.showMessageDialog(view, "Please select a directory first!");
				return;
			}
			
			CoordinateToMatlabExport ce = new CoordinateToMatlabExport();
			File dest = AskFileDialog.askMatFile(fn.getFile(), view);
			if (dest == null) return;
			try {
				ce.export(fn.getFile(), dest);
				JOptionPane.showMessageDialog(view, "Export OK!");
			} catch (IOException ioe) {
				JOptionPane.showMessageDialog(view, ioe.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
			}
		}
		
		else if (e.getActionCommand().equals("EXPORT.IMAGE")) {
			
			FileNode fn = jfe.getSelectedFile();
			if (fn == null || fn.getFile() == null) { // || !fn.getFile().isFile()) {
				JOptionPane.showMessageDialog(view, "Please select a file or a directory first!");
				return;
			}
			
			try {
				ImageToEPSExport.export(fn.getFile(), view);
				JOptionPane.showMessageDialog(view, "Export OK!");
			} catch (Exception e1) {
				JOptionPane.showMessageDialog(view, "Error: " + e1.getMessage());
				e1.printStackTrace();
			}
		}
		else if (e.getActionCommand().equals("EXPORT.PNG")) {
		}
		else if (e.getActionCommand().equals("EXPORT.MATLAB")) {
			
			FileNode fn = jfe.getSelectedFile();
			if (fn == null || fn.getFile() == null) { // || !fn.getFile().isFile()) {
				JOptionPane.showMessageDialog(view, "Please select a file or a directory first!");
				return;
			}
			
			try {
				File dest = AskFileDialog.askMatFile(fn.getFile(), view);
				if (dest == null) return;
				StatisticsToMatlabExport.export(fn.getFile(), dest);
				JOptionPane.showMessageDialog(view, "Export OK!");
			} catch (Exception e1) {
				JOptionPane.showMessageDialog(view, "Error: " + e1.getMessage());
				e1.printStackTrace();
			}
		}

		else if (e.getActionCommand().equals("EXPORT.MATLAB2")) {
			
			FileNode fn = jfe.getSelectedFile();
			if (fn == null || fn.getFile() == null) { // || !fn.getFile().isFile()) {
				JOptionPane.showMessageDialog(view, "Please select a file or a directory first!");
				return;
			}

			JFileChooser jfc = new JFileChooser(System.getProperty("user.home"));
			jfc.setFileFilter(ImageToEPSExport.dirFilter);
			jfc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
			int r = jfc.showSaveDialog(view);
			if (r != JFileChooser.APPROVE_OPTION) { return; }
			
			
			try {
				for (File f : fn.getFile().listFiles(FileFilters.ascFilter)) {
					
					String fileName = "neuro_export.mat";
					
					// suggest file with same name as neuron, but with .mat ending
					int lastDotIdx = f.getName().lastIndexOf('.'); 
					fileName = f.getName() + ".mat";
					if (lastDotIdx != -1) fileName = f.getName().substring(0, lastDotIdx) + ".mat";

					Network net = NeuroLucidaLoader.load(f);
					Neuron n = net.get(0);
					NeuronFilter.filterNeuron(n);
					NeuronToMatlabExport mat = new NeuronToMatlabExport(new File(jfc.getSelectedFile(), fileName), n, AuxData.getPlanes(f));
					mat.export();
				}
				JOptionPane.showMessageDialog(view, "Export OK!");
				
			} catch (Exception e1) {
				JOptionPane.showMessageDialog(view, "Error: " + e1.getMessage());
				e1.printStackTrace();
			}
		}
		
		else if (e.getActionCommand().equals("EXAMINE.XYZ")) {
			Neuron n = view.getOpenNeuron();
			JFrame jf = new JFrame("Planar view");
			jf.setLayout(new BorderLayout());
			jf.add(new JNeuroCoordScatter(n, 0), BorderLayout.WEST);
			jf.add(new JNeuroCoordScatter(n, 1), BorderLayout.EAST);
			jf.setSize(1200, 600);
			jf.setVisible(true);
		}

		else if (e.getActionCommand().equals("HIGHLIGHT")) {
			String res = JOptionPane.showInputDialog("Highlight terminal segments shorter than [um]:", "50");
			try {
				if (res != null) {
					double v = Double.parseDouble(res);
					NeuronJ3D.termSegLenThreshold = v;
				}
			} catch (NumberFormatException nfe) {
				JOptionPane.showMessageDialog(view, "Number error: " + nfe.getMessage());
			}
		}
		
		else if (e.getActionCommand().equals("FILTER.SHORT_TS")) {
			NeuronFilter.enabled = filter.isSelected();
		}

		else if (e.getActionCommand().equals("ABOUT")) {
			try {
				MetaInfo info = new MetaInfo(neuron.AbstractFiber.class);
				JFrame jf = new JFrame("About..");
				JTabbedPane jtp = new JTabbedPane();

				JLabel about = new JLabel();
				about.setText("Version: " + info.manifest.get("Implementation-Version"));
				jtp.addTab("About", about);

				JLabel test = new JLabel();
				JTable tbl = new JTable();
				int row = 0;
				for (String k : info.testsuite.keySet()) {
					int col = 0;
					tbl.getModel().setValueAt(test.getText(), row, col++);
					for (String s : info.testsuite.get(k)) tbl.getModel().setValueAt(test.getText(), row, col++);
					row++;
//					test.setText(test.getText() + k + " : " + info.testsuite.get(k) + "\n");
				}
				jtp.addTab("Tests", tbl);
				jf.setContentPane(jtp);
				jf.pack();
				jf.setVisible(true);
			}
			catch (Exception ex) {
				ex.printStackTrace();
			}

		}
		else if (e.getActionCommand().equals("EXPORT.HOC")) {

            JFileChooser jfc = new JFileChooser(System.getProperty("user.home"));
            jfc.setFileFilter(NeuronToHOCExport.hocFilter);
				jfc.setDialogTitle("Save " + (view.getOpenNeuron() != null ? "single HOC" : "multiple HOCs"));

            // show dialog
            int r = jfc.showSaveDialog(view);

            // if save pressed, save to file
            if (r == JFileChooser.APPROVE_OPTION) {

                if (view.getOpenNeuron() != null) {
						 JOptionPane.showMessageDialog(view, "Exporting single neuron to HOC.");
                    NeuronToHOCExport hw = new NeuronToHOCExport();
                    hw.outputNeuron(view.getOpenNeuron());
                    try {
                        hw.save(jfc.getSelectedFile());
                    } catch (IOException e1) {
                        // TODO Auto-generated catch block
                        e1.printStackTrace();
                    }
                } else if (view.getOpenNetwork() != null) {
						 JOptionPane.showMessageDialog(view, "Exporting " + view.getOpenNetwork().size() + " neuron(s) to HOC(s).");
                    int ni = 0;
                    String basename = jfc.getSelectedFile().getAbsolutePath().replaceAll("\\..*$", "");
                    for (Neuron n : view.getOpenNetwork()) {
								try {
									NeuronToHOCExport hw = new NeuronToHOCExport();
									hw.outputNeuron(n, true, true, false);
									 hw.save(new File(basename + "_" + ni + "_b.hoc"));

									 hw = new NeuronToHOCExport();
									hw.outputNeuron(n, true, false, true);
									 hw.save(new File(basename + "_" + ni + "_a.hoc"));
								} catch (IOException e1) {
									 e1.printStackTrace();
								}

                        ni++;
                    }
                }
			}
		}
	}
	
	public void fileSelected(ActionEvent e) {
//		double[] planes = new double[] { 680, 76, 0, -902 };
		try {
			File f = (File)e.getSource();
			if (f.isFile()) {
				// open file
				view.openFile(f, AuxData.getPlanes(f));

				if (view.getOpenNeuron() != null) {
					NeuronStatistics ns = view.getOpenNeuron().statistics();

					// sholl
					System.out.print("Basal Sholl : ");
					for (int d : ns.basalSholl(10)) System.out.print(d + ", ");
					System.out.println("");

					System.out.print("Neuron Sholl : ");
					for (int d : ns.neuronSholl(10)) System.out.print(d + ", ");
					System.out.println("");

					// branching
					System.out.println("Basal branching " + ns.basalBranchAngles().formatNormDist());
					System.out.println("Oblique branching " + ns.obliqueBranchAngles().formatNormDist());
					System.out.println("Tuft branching " + ns.tuftBranchAngles().formatNormDist());

					// turning
					System.out.println("Basal turning " + ns.basalTurningAngles().formatNormDist());
					System.out.println("Oblique turning " + ns.obliqueTurningAngles().formatNormDist());
					System.out.println("Tuft turning " + ns.tuftTurningAngles().formatNormDist());
				}
			}
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		} catch (IOException e1) {
			e1.printStackTrace();
		} catch (ParseException e1) {
			e1.printStackTrace();
		} 
	}	
	ActionListener fileSelectionListener = new ActionListener() {
		@Override
		public void actionPerformed(ActionEvent e) { fileSelected(e); }
	};
	
	JToggleButton filter;
		 JFrame frame;
	private boolean checkErrorOnLoad = false;


		 ActionListener menuListener = new ActionListener() {

		public void actionPerformed(ActionEvent ae) {

			if (ae.getActionCommand().equalsIgnoreCase("EXIT")) {
				frame.setVisible(false);
				System.exit(0);
			}
		}
	};


	public NeuroviewMain() {
		frame = new JFrame("Neuroview v2010-11-01 (C) Risto Laakso 2010");
		frame.setSize(800,600);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setLayout(new BorderLayout());
		
		// west - files
		jfe = new JFileExplorer();
		jfe.setFilter(JFileExplorer.neuroFilter);
		jfe.setPreferredSize(new Dimension(250,600));
//		JScrollPane jsp = new JScrollPane(jfe);
		jfe.setSelectionListener(fileSelectionListener);
//		jsp.setPreferredSize(new Dimension(200,600));
//		jsp.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
		frame.add(jfe, BorderLayout.WEST);
		
		// center - view
		view = new JNeuroView();
	/*	JPanel jp = new JPanel(); // kludge to make canvas3d not overlap menus
		jp.setLayout(new BorderLayout());
		jp.add(view, BorderLayout.CENTER);
		frame.add(jp, BorderLayout.CENTER);*/
		frame.add(view, BorderLayout.CENTER);
		
		// east - statistics
		
//		view.addCustomToolbarButton(new ImageIcon("icons/spreadsheet-icon.png"), "Export directory statistics..", "EXPORT.STATS", this);

	//	view.addCustomToolbarButton(new ImageIcon("icons/Save24.gif"), "Export directory coordinates..", "EXPORT.COORDS", this);

		view.addCustomToolbarButton(new ImageIcon("icons/Zoom24.gif"), "Examine XY & XZ planes..", "EXAMINE.XYZ", this);

		//view.addCustomToolbarButton(new ImageIcon("icons/Save24.gif"), "Export neuron as an image..", "EXPORT.IMAGE", this);

//		view.addCustomToolbarButton(new ImageIcon("icons/Save24.gif"), "Export neuron stats to matlab..", "EXPORT.MATLAB", this);

//		view.addCustomToolbarButton(new ImageIcon("icons/Save24.gif"), "Export neuron morphologies to matlab..", "EXPORT.MATLAB2", this);

//		view.addCustomToolbarButton(new ImageIcon("icons/Save24.gif"), "Export neuron morphologies to NEURON (HOC)..", "EXPORT.HOC", this);

		view.addCustomToolbarButton(new ImageIcon("icons/Find24.gif"), "Highlight short terminal segments..", "HIGHLIGHT", this);

//		filter = (JToggleButton) view.addCustomToolbarButton(null, "FILT", "Filter short terminal segments from neurons", "FILTER.SHORT_TS", this, JToggleButton.class);
		
//		view.addCustomToolbarButton(new ImageIcon("icons/Find24.gif"), "About..", "ABOUT", this);



		// Menu
		JPopupMenu.setDefaultLightWeightPopupEnabled(false); // canvas3d would overlap menus
		  JMenuBar bar = new JMenuBar();
		  JMenu menu = new JMenu("File"); bar.add(menu);

		  JMenuItem item;

		  /*= new JMenuItem("Load config..");
		  item.setActionCommand("LOAD.CONFIG");
		  item.addActionListener(menuListener);
		  menu.add(item);

		  item = new JMenuItem("Save config..");
		  item.setActionCommand("SAVE.CONFIG");
		  item.addActionListener(menuListener);
		  menu.add(item);
*/

		  item = new JMenuItem("Exit");
		  item.setActionCommand("EXIT");
		  item.addActionListener(menuListener);
		  menu.add(item);



		  menu = new JMenu("Options"); bar.add(menu);

		  final JCheckBoxMenuItem checkErrors = new JCheckBoxMenuItem("Check for reconstruction errors on load");
		  checkErrors.addChangeListener(new ChangeListener() {

			public void stateChanged(ChangeEvent ce) {
				checkErrorOnLoad = checkErrors.getState();
			}
			});
		  menu.add(checkErrors);

		  final JCheckBoxMenuItem filterShort = new JCheckBoxMenuItem("Filter short terminal segments from neurons");
		  filterShort.addChangeListener(new ChangeListener() {

			public void stateChanged(ChangeEvent ce) {
				NeuronFilter.enabled = filterShort.getState();
			}
			});
		  menu.add(filterShort);

		  item = new JMenuItem("Preferences..");
		  item.setActionCommand("PREF");
		  item.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent ae) {
				NeuroviewPreferences pref = new NeuroviewPreferences();
				pref.setVisible(true);
			}
		});
		  menu.add(item);


		  menu = new JMenu("Statistics"); bar.add(menu);

		  item = new JMenuItem("***");
		  item.setActionCommand("XX");
		  item.addActionListener(menuListener);
		  menu.add(item);


		  menu = new JMenu("Export"); bar.add(menu);

		  item = new JMenuItem("Export all in directory statistics to Excel (*.XLS)..");
		  item.setActionCommand("EXPORT.STATS");
		  item.addActionListener(this);
		  menu.add(item);

		  item = new JMenuItem("Export all in directory morphologies to Matlab (*.MAT)..");
		  item.setActionCommand("EXPORT.COORDS");
		  item.addActionListener(this);
		  menu.add(item);

		  menu.addSeparator();

		  item = new JMenuItem("Export neuron as an image (*.EPS)..");
		  item.setActionCommand("EXPORT.IMAGE");
		  item.addActionListener(this);
		  menu.add(item);

		  item = new JMenuItem("Export neuron statistics to Matlab (*.MAT)..");
		  item.setActionCommand("EXPORT.MATLAB");
		  item.addActionListener(this);
		  menu.add(item);

		  item = new JMenuItem("Export neuron morphology to Matlab (*.MAT)..");
		  item.setActionCommand("EXPORT.MATLAB2");
		  item.addActionListener(this);
		  menu.add(item);

		  item = new JMenuItem("Export neuron morphology to NEURON (*.HOC)..");
		  item.setActionCommand("EXPORT.HOC");
		  item.addActionListener(this);
		  menu.add(item);


		  bar.add(Box.createHorizontalGlue());
		  menu = new JMenu("Help"); bar.add(menu);

		  item = new JMenuItem("About..");
		  item.setActionCommand("ABOUT");
		  item.addActionListener(this);
		  menu.add(item);

		  bar.setDoubleBuffered(false);
		  frame.setJMenuBar(bar);


		frame.setVisible(true);
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		new NeuroviewMain();
	}

}
